home *** CD-ROM | disk | FTP | other *** search
/ Aminet 13 / Aminet 13 - August 1996.iso / Aminet / gfx / conv / WhirlGIF20.lha / WhirlGIF / src / whirlgif.c < prev    next >
C/C++ Source or Header  |  1996-03-09  |  17KB  |  783 lines

  1.   
  2. /*
  3.  * whirlgif.c
  4.  *
  5.  * Copyright (C) 1995,1996 by Kevin Kadow (kadokev@msg.net)
  6.  * 
  7.  * Based on txtmerge.c
  8.  * Copyright (C) 1990,1991,1992,1993 by Mark Podlipec. 
  9.  * All rights reserved.
  10.  *
  11.  * This software may be freely copied, modified and redistributed
  12.  * without fee provided that this copyright notice is preserved 
  13.  * intact on all copies and modified copies.
  14.  * 
  15.  * There is no warranty or other guarantee of fitness of this software.
  16.  * It is provided solely "as is". The author(s) disclaim(s) all
  17.  * responsibility and liability with respect to this software's usage
  18.  * or its effect upon hardware or computer systems.
  19.  *
  20.  */
  21.  /*
  22.   *
  23.   * Amiga Version by Lars Eilebrecht (sfx@unix-ag.uni-siegen.de)
  24.   *
  25.   */
  26.  /*
  27.   * Description:
  28.   *
  29.   * This program reads in a sequence of single-image GIF format files and
  30.   * outputs a single multi-image GIF file, suitable for use as an animation.
  31.   *
  32.   * TODO:
  33.   *
  34.   * More options for dealing with the colormap
  35.   *
  36.   * Eventually, I'd like to have this program compare the current image
  37.   * with the previous image and check to see if only a small section of
  38.   * the screen changed from the previous image. Worth a shot.
  39.   */
  40.  /*
  41.   * Rev 2.00    05Feb96 Kevin Kadow
  42.   *    transparency, gif comments,
  43.   * Rev 1.10    29Jan96 Kevin Kadow
  44.   *    first release of whirlgif
  45.   *
  46.   * txtmerge:
  47.   * Rev 1.00    23Jul91    Mark Podlipec
  48.   *    creation
  49.   * Rev 1.01    08Jan92    Mark Podlipec
  50.   *     use all colormaps, not just 1st.
  51.   *
  52.   * 
  53.   */
  54. #define DA_REV 2.00
  55.  
  56. #include "whirlgif.h"
  57.  
  58. #define MAXVAL  4100            /* maxval of lzw coding size */
  59. #define MAXVALP 4200
  60.  
  61. static const char ver[]="$VER: WhirlGIF 2.0 (05.02.96) © 1996 by Kevin Kadow, © 1991-1992 by Mark Podlipec (Amiga Version by Lars Eilebrecht)\0";
  62.  
  63. /*
  64.  * Set some defaults, these can be changed on the command line
  65.  */
  66. unsigned int loop=DEFAULT_LOOP,loopcount=0,
  67.     use_colormap=DEFAULT_USE_COLORMAP,
  68.     debug_flag=0,
  69.     verbose=0;
  70.  
  71. int imagex = 0;
  72. int imagey = 0;
  73. int imagec = 0;
  74.  
  75. /* global settings for offset, transparency */
  76. Global global;
  77.  
  78. GIF_Color gif_cmap[256];
  79.  
  80. GIF_Screen_Hdr gifscrn;
  81. GIF_Image_Hdr gifimage;
  82. GIF_Table table[MAXVALP];
  83.  
  84. ULONG root_code_size,code_size,CLEAR,EOI,INCSIZE;
  85. ULONG nextab;
  86. ULONG gif_mask[16] = {1,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0};
  87. ULONG gif_ptwo[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
  88.  
  89.  
  90. UBYTE gif_buff[MAXVALP];
  91. ULONG gif_block_size;
  92. int num_bits,bits;
  93.  
  94. int pic_i;
  95. char gif_file_name[BIGSTRING];
  96. int screen_was_last;
  97.  
  98.  
  99. void TheEnd()
  100. {
  101.  exit(0);
  102. }
  103.  
  104. void TheEnd1(p)
  105. char *p;
  106. {
  107.  fprintf(stderr,"%s",p);
  108.  TheEnd();
  109. }
  110.  
  111. void Usage()
  112. {
  113.   fprintf(stderr,"\nUsage: whirlgif [-o outfile] [-loop [count]] [-time #delay]\n");
  114.   fprintf(stderr,"\t[ -i listfile] file1 [ -time #delay] file2 ...\n");
  115.   exit(0);
  116. }
  117.  
  118. main(argc,argv)
  119. int argc;
  120. char *argv[];
  121. {
  122.  FILE * infile, *fout;
  123.  char temp[BIGSTRING];
  124.  int i;
  125.  int count=0;
  126.  
  127.  fprintf(stderr,"whirlgif Rev %2.2f (C) 1996 by Kevin Kadow\n",DA_REV);
  128.  fprintf(stderr,"                  (C) 1991,1992 by Mark Podlipec\n");
  129.  
  130.  if (argc < 2) Usage();
  131.  
  132.  /* set global values */
  133.  screen_was_last = FALSE;
  134.  global.trans.type=TRANS_NONE;
  135.  global.trans.valid=FALSE;
  136.  global.time=DEFAULT_TIME;
  137.  global.left=0;
  138.  global.top=0;
  139.  
  140.  
  141.  fout=stdout;
  142.  i = 1;
  143.  while( i < argc)
  144.  {
  145.   char *p;
  146.   p = argv[i];
  147.   /*fprintf(stderr,"Option: %s\n",p);*/
  148.   if ( (p[0] == '-') || (p[0] == '+') )
  149.   { 
  150.    ++p; /* strip off the - */
  151.    switch(p[0])
  152.    {
  153.     case 'v':    /* Give lots of information */
  154.             verbose++;
  155.             i++;
  156.         break;
  157.  
  158.     case 'd':    /* Debug mode */
  159.         debug_flag++;
  160.         i++;
  161.         fprintf(stderr,"DEBUG: Debug Level %d\n",debug_flag);
  162.         break;
  163.  
  164.     case 'l':    /* Enable looping */
  165.         loop=TRUE;
  166.         i++;
  167.         if(*argv[i] !='-') {
  168.           /* a loop count was given */
  169.           loopcount=atoi(argv[i++]);
  170.           if(verbose) fprintf(stderr,"Loop %d times\n",loopcount);
  171.           }
  172.         else {
  173.           /* default to infinite loop */
  174.           loopcount=0;
  175.           if(verbose) fputs("Looping enabled\n",stderr);
  176.           }
  177.         break;
  178.  
  179.     case 'u':    /* Use colormap? true or false */
  180.         i++;
  181.         if(atoi(argv[i]) || !strcmp("true",argv[i])) use_colormap=1;
  182.         else use_colormap=0;
  183.         i++;
  184.         break;
  185.  
  186.     case 't':    /* either time or transparent */
  187.         i++;
  188.         if(!strcmp("time",p)) {
  189.             /* Delay time in 1/100's of a second */
  190.             global.time=atoi(argv[i++]);
  191.             }
  192.         else if(!strncmp("trans",p,4)) Calc_Trans(argv[i++]);
  193.         break;
  194.  
  195.     case 'o':    /* Output file - send output to a given filename */
  196.         i++;
  197.         if(!strncmp("off",p,3)) set_offset(argv[i]);
  198.         else
  199.         /* It must be 'output, so do that */
  200.         if(NULL==(fout=fopen(argv[i],"w")))
  201.             {
  202.             fprintf(stderr,"Cannot open %s for output\n",argv[i]);
  203.             exit(1);
  204.             }
  205.         i++;
  206.         break;
  207.     case 'i':    /* input file - file with a list of images */
  208.         i++;
  209.         if(NULL != (infile=fopen(argv[i],"r"))) {
  210.             while(fgets(gif_file_name,BIGSTRING,infile)) {
  211.                 strtok(gif_file_name,"\n");
  212.                   if(!count) GIF_Read_File(fout,gif_file_name,1);
  213.                   else       GIF_Read_File(fout,gif_file_name,0);
  214.                   count++;
  215.                 }
  216.             fclose(infile);
  217.             }
  218.         else fprintf(stderr,"Cannot read list file %s\n",argv[i]);
  219.         i++;
  220.         break;
  221.     default: 
  222.         Usage();
  223.         exit(0);
  224.         break;
  225.    }
  226.    continue;
  227.   }
  228.   /* Not an option, must be the name of an input file */
  229.   if(!count) GIF_Read_File(fout,argv[i],1);
  230.   else       GIF_Read_File(fout,argv[i],0);
  231.   count++;
  232.   i++;
  233.  }
  234.  /* We're done with all the options, finish up */
  235.  if(count >0)
  236.   {
  237.   fputc(';',fout); /* image separator */
  238.   sprintf(temp,"whirlgif %2.2f (C) kadokev@msg.net. %d images",DA_REV,count);
  239.   GIF_Comment(fout,temp);
  240.   }
  241.  
  242.  fclose(fout);
  243.  fprintf(stderr,"Processed %d files.\n",count);
  244. }
  245.  
  246.  
  247. /*
  248.  * Read a GIF file, outputting to fname as we go.
  249.  * It would be faster to read and write the individual blocks,
  250.  * but eventually we'd like to optimize based on changes from
  251.  * previous images(ie only a small section of the image changed.
  252.  */
  253. void
  254. GIF_Read_File(fout,fname,first_image)
  255. FILE * fout;
  256. char *fname;
  257. int first_image;
  258. {
  259.  FILE *fp;
  260.  int i;
  261.  
  262.  if ( (fp=fopen(fname,"r"))==0)
  263.  { 
  264.   fprintf(stderr,"Can't open %s for reading.\n",fname); 
  265.   TheEnd();
  266.  }
  267.  
  268.  GIF_Screen_Header(fp,fout,first_image);
  269.  
  270.  /*** read until  ,  separator */
  271.  do
  272.  {
  273.   i=fgetc(fp);
  274.   if ( (i<0) && feof(fp))
  275.   {
  276.    fclose(fp);
  277.    TheEnd1("GIF_Read_Header: Unexpected End of File\n");
  278.   }
  279.  } while(i != ',');
  280.  
  281.  if(first_image)
  282.   {
  283.    /* stuff we only do once */
  284.    if(loop) GIF_Loop(fout,loopcount);
  285.    }
  286.  if(global.time||(global.trans.type!=TRANS_NONE && global.trans.valid))
  287.     GIF_GCL(fout,global.time);
  288.  
  289.  fputc(',',fout); /* image separator */
  290.  
  291.  GIF_Image_Header(fp,fout,first_image);
  292.  
  293.  /*FOO*/
  294.  
  295.  /*** Setup ACTION for IMAGE */
  296.  
  297.  GIF_Decompress(fp,fout);
  298.  fputc(0,fout);  /* block count of zero */
  299.  
  300.  fclose(fp);
  301. }
  302.  
  303. void GIF_Decompress(fp,fout)
  304. FILE *fp,*fout;
  305. {
  306.  register ULONG code,old;
  307.  
  308.  pic_i = 0;
  309.  bits=0;
  310.  num_bits=0;
  311.  gif_block_size=0;
  312.     /* starting code size of LZW */
  313.  root_code_size=(fgetc(fp) & 0xff); fputc(root_code_size,fout);
  314.  GIF_Clear_Table();                /* clear decoding symbol table */
  315.  
  316.  code=GIF_Get_Code(fp,fout);
  317.  
  318.  if (code==CLEAR) 
  319.  {
  320.   GIF_Clear_Table(); 
  321.   code=GIF_Get_Code(fp,fout);
  322.  }
  323.  /* write code(or what it currently stands for) to file */
  324.  GIF_Send_Data(code);   
  325.  old=code;
  326.  code=GIF_Get_Code(fp,fout);
  327.  do
  328.  {
  329.   if (table[code].valid==1)    /* if known code */
  330.   {
  331.        /* send it's associated string to file */
  332.     GIF_Send_Data(code);
  333.     GIF_Get_Next_Entry(fp);       /* get next table entry (nextab) */
  334.     GIF_Add_To_Table(old,code,nextab);  /* add old+code to table */
  335.     old=code;
  336.   }
  337.   else      /* code doesn't exist */
  338.   {
  339.     GIF_Add_To_Table(old,old,code);   /* add old+old to table */
  340.     GIF_Send_Data(code);
  341.     old=code;
  342.   }
  343.   code=GIF_Get_Code(fp,fout);
  344.   if (code==CLEAR)
  345.   { 
  346.    GIF_Clear_Table();
  347.    code=GIF_Get_Code(fp,fout);
  348.    GIF_Send_Data(code);
  349.    old=code;
  350.    code=GIF_Get_Code(fp,fout);
  351.   }
  352.  } while(code!=EOI);
  353. }
  354.  
  355. void GIF_Get_Next_Entry(fp)
  356. FILE *fp;
  357. {
  358.    /* table walk to empty spot */
  359.  while(  (table[nextab].valid==1)
  360.        &&(nextab<MAXVAL)
  361.       ) nextab++;
  362.  /* 
  363.   * Ran out of space??!?  Something's roached 
  364.   */
  365.  if (nextab>=MAXVAL)    
  366.  { 
  367.   fprintf(stderr,"Error: GetNext nextab=%ld\n",nextab);
  368.   fclose(fp);
  369.   TheEnd();
  370.  }
  371.  if (nextab==INCSIZE)   /* go to next table size (and LZW code size ) */
  372.  {
  373.    /* fprintf(stderr,"GetNext INCSIZE was %ld ",nextab); */
  374.    code_size++; INCSIZE=(INCSIZE*2)+1;
  375.    if (code_size>=12) code_size=12;
  376. /*   fprintf(stderr,"<%ld>",INCSIZE); */
  377.  }
  378.  
  379. }
  380. /*  body is associated string
  381.     next is code to add to that string to form associated string for
  382.     index
  383. */     
  384.  
  385. void GIF_Add_To_Table(body,next,index)
  386. register ULONG body,next,index;
  387. {
  388.  if (index>MAXVAL)
  389.  { 
  390.   fprintf(stderr,"Error index=%ld\n",index);
  391.  }
  392.  else
  393.  {
  394.   table[index].valid=1;
  395.   table[index].data=table[next].first;
  396.   table[index].first=table[body].first;
  397.   table[index].last=body;
  398.  }
  399. }
  400.  
  401. void GIF_Send_Data(index)
  402. register int index;
  403. {
  404.  register int i,j;
  405.  i=0;
  406.  do         /* table walk to retrieve string associated with index */
  407.  { 
  408.   gif_buff[i]=table[index].data; 
  409.   i++;
  410.   index=table[index].last;
  411.   if (i>MAXVAL)
  412.   { 
  413.    fprintf(stderr,"Error: Sending i=%ld index=%ld\n",i,index);
  414.    TheEnd();
  415.   }
  416.  } while(index>=0);
  417.  
  418.  /* now invert that string since we retreived it backwards */
  419.  i--;
  420.  for(j=i;j>=0;j--)
  421.  {
  422.   /*pic[pic_i] = gif_buff[j] | gif_pix_offset;*/
  423.   pic_i++;
  424.  }
  425. }
  426.  
  427.  
  428. /* 
  429.  * initialize string table 
  430.  */
  431. void GIF_Init_Table()       
  432. {
  433.  register int maxi,i;
  434.  
  435. if (debug_flag) fprintf(stderr,"Initing Table...");
  436.  maxi=gif_ptwo[root_code_size];
  437.  for(i=0; i<maxi; i++)
  438.  {
  439.   table[i].data=i;   
  440.   table[i].first=i;
  441.   table[i].valid=1;  
  442.   table[i].last = -1;
  443.  }
  444.  CLEAR=maxi; 
  445.  EOI=maxi+1; 
  446.  nextab=maxi+2;
  447.  INCSIZE = (2*maxi)-1;
  448.  code_size=root_code_size+1;
  449. }
  450.  
  451.  
  452. /* 
  453.  * clear table 
  454.  */
  455. void GIF_Clear_Table()   
  456. {
  457.  register int i;
  458. if (debug_flag) fprintf(stderr,"Clearing Table...\n");
  459.  for(i=0;i<MAXVAL;i++) table[i].valid=0;
  460.  GIF_Init_Table();
  461. }
  462.  
  463. /*CODE*/
  464. ULONG GIF_Get_Code(fp,fout) /* get code depending of current LZW code size */
  465. FILE *fp,*fout;
  466. {
  467.  ULONG code;
  468.  register int tmp;
  469.  
  470.  while(num_bits < code_size)
  471.  {
  472.   /**** if at end of a block, start new block */
  473.   if (gif_block_size==0) 
  474.   {
  475.    tmp = fgetc(fp);
  476.    if (tmp >= 0 )
  477.    {
  478.     fputc(tmp,fout);
  479.     gif_block_size=(ULONG)(tmp);
  480.    }
  481.    else TheEnd1("EOF in data stream\n");
  482.   }
  483.  
  484.   tmp = fgetc(fp);   gif_block_size--;
  485.   if (tmp >= 0)
  486.   {
  487.    fputc(tmp,fout);
  488.    bits |= ( ((ULONG)(tmp) & 0xff) << num_bits );
  489.    num_bits+=8;
  490.   }
  491.   else TheEnd1("EOF in data stream\n");
  492.  }
  493.   
  494.  code = bits & gif_mask[code_size];
  495.  bits >>= code_size;
  496.  num_bits -= code_size; 
  497.  
  498.  
  499.  if (code>MAXVAL)
  500.  { 
  501.   fprintf(stderr,"\nError! in stream=%lx \n",code); 
  502.   fprintf(stderr,"CLEAR=%lx INCSIZE=%lx EOI=%lx code_size=%lx \n",
  503.                                            CLEAR,INCSIZE,EOI,code_size); 
  504.   code=EOI;
  505.  }
  506.  
  507.  if (code==INCSIZE)
  508.  {
  509.   if (code_size<12)
  510.   {
  511.    code_size++; INCSIZE=(INCSIZE*2)+1;
  512.   }
  513.   else if (debug_flag) fprintf(stderr,"<13?>"); 
  514.  }
  515.  
  516.  return(code);
  517. }
  518.  
  519.  
  520. /* 
  521.  * read GIF header 
  522.  */
  523. void GIF_Screen_Header(fp,fout,first_time)
  524. FILE *fp,*fout;
  525. int first_time;
  526. {
  527.  int temp,i;
  528.  
  529.  for(i=0;i<6;i++)
  530.  {
  531.   temp = fgetc(fp);
  532.   if(i==4 && temp == '7') temp='9';
  533.   if (first_time==TRUE) fputc(temp,fout);
  534.  }
  535.  
  536.  gifscrn.width  = GIF_Get_Short(fp,fout,first_time);
  537.  gifscrn.height = GIF_Get_Short(fp,fout,first_time);
  538.  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
  539.  gifscrn.m       =  temp & 0x80;
  540.  gifscrn.cres    = (temp & 0x70) >> 4;
  541.  gifscrn.pixbits =  temp & 0x07;
  542.  
  543.  gifscrn.bc  = fgetc(fp);
  544.  if (first_time==TRUE) 
  545.     {
  546.     /* we really should set the background color to the transparent color */
  547.     fputc(gifscrn.bc,fout);
  548.     }
  549.  
  550.  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
  551.  imagec=gif_ptwo[(1+gifscrn.pixbits)];
  552.  
  553.  if (verbose)
  554.   fprintf(stderr,"Screen: %ldx%ldx%ld m=%ld cres=%ld bkgnd=%ld pix=%ld\n",
  555.     gifscrn.width,gifscrn.height,imagec,gifscrn.m,gifscrn.cres,
  556.     gifscrn.bc,gifscrn.pixbits);
  557.  
  558.  if(global.trans.type==TRANS_RGB) global.trans.valid=0;
  559.  if (gifscrn.m)
  560.  {
  561.   for(i=0;i<imagec;i++)
  562.   {
  563.    gif_cmap[i].cmap.red   = temp = fgetc(fp); 
  564.            if (first_time==TRUE) fputc(temp,fout);
  565.    gif_cmap[i].cmap.green = temp = fgetc(fp); 
  566.            if (first_time==TRUE) fputc(temp,fout);
  567.    gif_cmap[i].cmap.blue  = temp = fgetc(fp); 
  568.            if (first_time==TRUE) fputc(temp,fout);
  569.  
  570.    if(global.trans.type==TRANS_RGB && !global.trans.valid)
  571.     if(global.trans.red==gif_cmap[i].cmap.red &&
  572.     global.trans.green==gif_cmap[i].cmap.green &&
  573.     global.trans.blue==gif_cmap[i].cmap.blue) {
  574.       if(debug_flag>1) fprintf(stderr," Transparent match at %d\n",i);
  575.         global.trans.map=i;
  576.     global.trans.valid=TRUE;
  577.     }
  578.   }
  579.  }
  580.  screen_was_last = TRUE;
  581. }
  582.  
  583. void GIF_Image_Header(fp,fout,first_time)
  584. FILE *fp,*fout;
  585. int first_time;
  586. {
  587.  int temp,tnum,i,r,g,b;
  588.  
  589.  gifimage.left   = GIF_Get_Short(fp,fout,1);
  590.  if(global.left) gifimage.left+=global.left;
  591.  
  592.  gifimage.top    = GIF_Get_Short(fp,fout,1);
  593.  if(global.top) gifimage.top+=global.top;
  594.  
  595.  gifimage.width  = GIF_Get_Short(fp,fout,1);
  596.  gifimage.height = GIF_Get_Short(fp,fout,1);
  597.  temp=fgetc(fp); 
  598.  
  599.  
  600.     
  601.  gifimage.i        = temp & 0x40;
  602.  gifimage.pixbits  = temp & 0x07;
  603.  gifimage.m        = temp & 0x80;
  604.  
  605.  /* this sets the local colormap bit to true */
  606.  if (screen_was_last && (first_time==FALSE)) temp |= 0x80;
  607.  
  608.  temp &= 0xf8;
  609.  temp |= gifscrn.pixbits;
  610.  fputc(temp,fout);
  611.  
  612.  imagex=gifimage.width;
  613.  imagey=gifimage.height;
  614.  tnum=gif_ptwo[(1+gifimage.pixbits)];
  615.  if (verbose)
  616.   fprintf(stderr,"Image: %ldx%ldx%ld (%d,%d) m=%ld i=%ld pix=%ld \n",
  617.     imagex,imagey,tnum,gifimage.left,gifimage.top,
  618.     gifimage.m,gifimage.i,gifimage.pixbits);
  619.  
  620.  /* if there is an image cmap, then use it */
  621.  
  622.  if (gifimage.m)
  623.  {
  624.   if(debug_flag) fprintf(stderr,"DEBUG:Transferring colormap of %d colors\n");
  625.   for(i=0;i<tnum;i++)
  626.   {
  627.    gif_cmap[i].cmap.red   = r = fgetc(fp);
  628.    gif_cmap[i].cmap.green = g = fgetc(fp);
  629.    gif_cmap[i].cmap.blue  = b = fgetc(fp);
  630.    fputc(r,fout);
  631.    fputc(g,fout);
  632.    fputc(b,fout);
  633.   }
  634.  }  /* else if screen was last not 1st time */
  635.  else if (screen_was_last && (first_time==FALSE))
  636.  {
  637.   if(debug_flag>1) fprintf(stderr,"DEBUG:Writing colormap of %d colors\n");
  638.   for(i=0;i<imagec;i++)
  639.   {
  640.    fputc(gif_cmap[i].cmap.red  ,fout);
  641.    fputc(gif_cmap[i].cmap.green,fout);
  642.    fputc(gif_cmap[i].cmap.blue ,fout);
  643.   }
  644.  }
  645.  screen_was_last = FALSE; 
  646. }
  647.  
  648.  
  649. /*
  650.  *
  651.  */
  652. int GIF_Get_Short(fp,fout,first_time)
  653. FILE *fp,*fout;
  654. int first_time;
  655. {
  656.  register int temp,tmp1;
  657.  temp=fgetc(fp);     if (first_time==TRUE) fputc(temp,fout);
  658.  tmp1=fgetc(fp);     if (first_time==TRUE) fputc(tmp1,fout);
  659.  return(temp|( (tmp1) << 8 ));
  660. }
  661.  
  662. void GIF_Comment(fout,string)
  663. FILE *fout;
  664. char *string;
  665. {
  666. if(!string || !strlen(string))
  667.         {
  668.         /* Bogus string */
  669.         if(debug_flag) fprintf(stderr,"GIF_Comment: invalid argument");
  670.         return;
  671.         }
  672. fputc(0x21,fout);
  673. fputc(0xF9,fout);
  674. fputs(string,fout);
  675. fputc(0,fout);
  676. }
  677.  
  678. /*
  679.  * Write a Netscape loop marker.
  680.  */
  681. void GIF_Loop(fout,repeats)
  682. FILE *fout;
  683. unsigned int repeats;
  684. {
  685. UBYTE low=0,high=0;
  686. if(repeats) {
  687.     /* non-zero repeat count- Netscape hasn't implemented this yet */
  688.     high=repeats / 256;
  689.     low=repeats % 256;
  690.     }
  691.  
  692. fputc(0x21,fout);
  693. fputc(0xFF,fout);
  694. fputc(0x0B,fout);
  695. fputs("NETSCAPE2.0",fout);
  696. fputc(0x03,fout);
  697. fputc(0x01,fout);
  698. fputc(0x00,fout);
  699.  
  700.  
  701. fputc(low,fout); /* the delay count - 0 for infinite */
  702. fputc(high,fout); /* second byte of delay count */
  703.  
  704. if(verbose) fprintf(stderr,"Wrote loop extension\n");
  705. }
  706.  
  707. /*
  708.  * GIF_GCL - add a Control Label to set an inter-frame delay value.
  709.  */
  710. void GIF_GCL(fout,delay)
  711. FILE * fout;
  712. unsigned int delay;
  713. {
  714. UBYTE low=0,high=0,flag=0;
  715. if(delay) {
  716.     /* non-zero delay, figure out low/high bytes */
  717.     high=delay / 256;
  718.     low=delay % 256;
  719.     }
  720.  
  721. fputc(0x21,fout);
  722. fputc(0xF9,fout);
  723. fputc(0x04,fout);
  724.  
  725. if(delay) flag |=0x80;
  726. if(global.trans.valid) flag |=0x01;
  727. fputc(flag,fout);
  728.  
  729. fputc(low,fout); /* the delay speed - 0 is instantaneous */
  730. fputc(high,fout); /* second byte of delay count */
  731.  
  732. fputc(global.trans.map,fout);
  733. fputc(0,fout);
  734. if(debug_flag>1) {
  735.   fprintf(stderr,"GCL: delay %d",delay);
  736.   if(global.trans.valid) fprintf(stderr," Transparent: %d",global.trans.map);
  737.   fputc('\n',stderr);
  738.   }
  739. }
  740.  
  741.  
  742. void Calc_Trans(string)
  743. char * string;
  744. {
  745. if(string[0] != '#') {
  746.   global.trans.type=TRANS_MAP;
  747.   global.trans.map=atoi(string);
  748.   global.trans.valid=1;
  749.   }
  750. else
  751.   {
  752.   /* it's an RGB value */
  753.   int r,g,b;
  754.   string++;
  755.   if(3==sscanf(string,"%2x%2x%2x",&r,&g,&b)) {
  756.     global.trans.red=r;
  757.     global.trans.green=g;
  758.     global.trans.blue=b;
  759.     global.trans.type=TRANS_RGB;
  760.     if(debug_flag) fprintf(stderr,"Transparent RGB=(%x,%x,%x)\n",r,g,b);
  761.     }
  762.   }
  763. if(debug_flag) fprintf(stderr,"DEBUG:Calc_trans is %d\n",global.trans.type);
  764. }
  765.  
  766. void set_offset(string)
  767. char * string;
  768. {
  769. char *off_x,*off_y;
  770. off_x=(char *) strtok(string,",");
  771. off_y=(char *)strtok((char *)NULL,",");
  772. if(off_x && off_y) {
  773.     /* set the offset */
  774.     global.left=atoi(off_x);
  775.     global.top=atoi(off_y);
  776.     if(debug_flag>1) fprintf(stderr,"Offset changed to %d,%d\n",
  777.         global.left,global.top);
  778.     return;
  779.     }
  780. if(debug_flag>1) fprintf(stderr,"Couldn't parse offset values.\n");
  781. exit(0);
  782. }
  783.